#include <Windows.h>
#include <WbemIdl.h>
#include <comutil.h>
#include <string.h>

#pragma comment(lib, "wbemuuid.lib")
#pragma comment(lib, "comsuppw.lib")

#define ECRAMWRAD 0x00000000000  // EC RAM Wirte address, value must move to high bit, such as move left 16bit <<16
#define ECRAMADDR 0x10000000000  // EC RAM Read  address, with offset such as [0x0454]
#define ECRAMVCMD 0x20000000000  // EC RAM Vitual Command, such as Fn+F1, Fn+F2

#define R	0x24
#define G	0x22
#define B	0x1B

IWbemServices	*m_pSvc;

#ifdef __cplusplus
extern "C"
{
#endif
	BOOL oops(const char *msg)
	{
		WCHAR mess[200];
		wsprintf(mess, L"%s\n(error code: %d)", msg, GetLastError());
		MessageBox(NULL, mess, L"Error", 0);
		fprintf(stderr, "%s", msg);
		exit(1);
	}

	BOOL InitWMI(void)
	{
		LRESULT hr = CoInitializeSecurity(
			NULL,
			-1,                          // COM negotiates service  
			NULL,                        // Authentication services  
			NULL,                        // Reserved  
			RPC_C_AUTHN_LEVEL_DEFAULT,   // Default authentication   
			RPC_C_IMP_LEVEL_IMPERSONATE, // Default Impersonation    
			NULL,                        // Authentication info  
			EOAC_NONE,                   // Additional capabilities   
			NULL                         // Reserved  
		);
	
		if (FAILED(hr))
			oops("CoInitializeSecurity()");
	
		// Step 3: ---------------------------------------------------
		// Obtain the initial locator to WMI -------------------------
		IWbemLocator *pLoc = NULL;
		hr = CoCreateInstance(
			CLSID_WbemLocator,
			0,
			CLSCTX_INPROC_SERVER,
			IID_IWbemLocator, (LPVOID *)&pLoc);
	
		if (FAILED(hr))
			oops("CoCreateInstance()");
	
		// Step 4: ---------------------------------------------------
		// Connect to WMI through the IWbemLocator::ConnectServer method
		hr = pLoc->ConnectServer(
			_bstr_t(L"ROOT\\WMI"),		   // Object path of WMI namespace
			NULL,                          // User name. NULL = current user
			NULL,                          // User password. NULL = current
			0,                             // Locale. NULL indicates current
			NULL,                          // Security flags.
			0,                             // Authority (e.g. Kerberos)
			0,                             // Context object 
			&m_pSvc                        // pointer to IWbemServices proxy
		);
	
		if (pLoc != NULL)
		{
			pLoc->Release();
			pLoc = NULL;
		}
	
		if (FAILED(hr))
			oops("ConnectServer()");
	
		hr = CoSetProxyBlanket(
			m_pSvc,                      // Indicates the proxy to set
			RPC_C_AUTHN_WINNT,           // RPC_C_AUTHN_xxx
			RPC_C_AUTHZ_NONE,            // RPC_C_AUTHZ_xxx
			NULL,                        // Server principal name 
			RPC_C_AUTHN_LEVEL_CALL,      // RPC_C_AUTHN_LEVEL_xxx 
			RPC_C_IMP_LEVEL_IMPERSONATE, // RPC_C_IMP_LEVEL_xxx
			NULL,                        // client identity
			EOAC_NONE                    // proxy capabilities 
		);
		if (FAILED(hr))
			oops("CoSetProxyBlanket()");
		return TRUE;
	}
	
	bool WmiEcVitualCmd(ULONG64 ullAddr, ULONG Value)
	{
		// Step 6: --------------------------------------------------
		// Use the IWbemServices pointer to make requests of WMI ----
	
		ullAddr = ECRAMVCMD + Value;
	
		IEnumWbemClassObject *pEnum = NULL;
		LRESULT hres = m_pSvc->ExecQuery(_bstr_t(L"WQL"), //Query Language
			_bstr_t(L"Select * from AcpiTest_MULong"),    //Query to Execute
			WBEM_FLAG_RETURN_IMMEDIATELY,                 //Make a semi-synchronous call
			NULL,                                         //Context
			&pEnum                                        //Enumeration Interface
		);
	
		ULONG ulReturned;
		IWbemClassObject *pObj = NULL;
	
		//Get the Next Object from the collection 
		hres = pEnum->Next(WBEM_INFINITE, //Timeout
			1,                            //No of objects requested
			&pObj,                        //Returned Object 
			&ulReturned                   //No of object returned
		);
	
		if (FAILED(hres))
		{
			printf("\nWBEM_INFINITE Error: %d", GetLastError());
			return FALSE;
		}
	
		// Get Instance name path
		VARIANT varInstaceName;
		VariantInit(&varInstaceName);
		hres = pObj->Get(_bstr_t(L"__PATH"), 0, &varInstaceName, NULL, NULL);
		if (FAILED(hres))
		{
			VariantClear(&varInstaceName);
			printf("\n__PATH Error: %d", GetLastError());
			return FALSE;
		}
	
		pObj->Release();
		pObj = NULL;
	
		IWbemClassObject* pClass = NULL;
		hres = m_pSvc->GetObject(_bstr_t(L"AcpiTest_MULong"),/* ClassName */
			0,
			NULL,
			&pClass,
			NULL);
		if (FAILED(hres))
		{
			VariantClear(&varInstaceName);
			printf("\nAcpiTest_MULong Error: %d", GetLastError());
			return FALSE;
		}
	
		IWbemClassObject* pInParamsDefinition = NULL;
		hres = pClass->GetMethod(_bstr_t(L"GetSetULong"), /* MethodName */
			0,
			&pInParamsDefinition,
			NULL);
		if (FAILED(hres))
		{
			VariantClear(&varInstaceName);
			printf("\nGetSetULong Error: %d", GetLastError());
			return FALSE;
		}
	
		pClass->Release();
		pClass = NULL;
	
		IWbemClassObject* pClassInstance = NULL;
		hres = pInParamsDefinition->SpawnInstance(0, &pClassInstance);
		pInParamsDefinition->Release();
		pInParamsDefinition = NULL;
	
		// Create the values for the in parameters
		TCHAR szCmd[20] = { NULL };
		wsprintf(szCmd, L"%I64u", ullAddr);
		//printf("\nAddr: %0IX", ullAddr);
	
		VARIANT varCommand;
		VariantInit(&varCommand);
		varCommand.vt = VT_BSTR;
		varCommand.bstrVal = _bstr_t(szCmd);
		hres = pClassInstance->Put(L"Data", 0, &varCommand, CIM_UINT64);
		if (FAILED(hres))
		{
			VariantClear(&varCommand);
			VariantClear(&varInstaceName);
			printf("\nData Error: %d", GetLastError());
			return FALSE;
		}
	
		// Execute Method
		IWbemClassObject* pOutParams = NULL;
		hres = m_pSvc->ExecMethod(varInstaceName.bstrVal,
			_bstr_t(L"GetSetULong"), /* MethodName*/
			0,
			NULL,
			pClassInstance,
			&pOutParams,
			NULL);
		if (FAILED(hres))
		{
			VariantClear(&varCommand);
			VariantClear(&varInstaceName);
			pClassInstance->Release();
			printf("\nGetSetULong1 Error: %d", GetLastError());
			return FALSE;
		}
	
		// use the following code.  The return value will be in &varReturn
		VARIANT varReturnValue;
		VariantInit(&varReturnValue);
		hres = pOutParams->Get(_bstr_t(L"Return"), 0, &varReturnValue, NULL, 0);
		if (FAILED(hres))
		{
			VariantClear(&varReturnValue);
			VariantClear(&varInstaceName);
			pClassInstance->Release();
			return FALSE;
		}
	
		//*lpVal = varReturnValue.bVal;
		//printf("--> %#02x", *lpVal);
	
		// Clean up
		VariantClear(&varCommand);
		VariantClear(&varInstaceName);
		VariantClear(&varReturnValue);
		pClassInstance->Release();
		pOutParams->Release();
	
		return TRUE;
	}
	
	bool WmiReadEcRam(ULONG64 ullAddr, BYTE *lpVal)
	{
		// Step 6: --------------------------------------------------
		// Use the IWbemServices pointer to make requests of WMI ----
	
		ullAddr = ECRAMADDR + ullAddr;
	
		IEnumWbemClassObject *pEnum = NULL;
		LRESULT hres = m_pSvc->ExecQuery(_bstr_t(L"WQL"), //Query Language
			_bstr_t(L"Select * from AcpiTest_MULong"),    //Query to Execute
			WBEM_FLAG_RETURN_IMMEDIATELY,                 //Make a semi-synchronous call
			NULL,                                         //Context
			&pEnum                                        //Enumeration Interface
		);
	
		ULONG ulReturned;
		IWbemClassObject *pObj = NULL;
	
		//Get the Next Object from the collection 
		hres = pEnum->Next(WBEM_INFINITE, //Timeout
			1,                            //No of objects requested
			&pObj,                        //Returned Object 
			&ulReturned                   //No of object returned
		);
	
		if (FAILED(hres))
		{
			printf("\nWBEM_INFINITE Error: %d", GetLastError());
			return FALSE;
		}
	
		// Get Instance name path
		VARIANT varInstaceName;
		VariantInit(&varInstaceName);
		hres = pObj->Get(_bstr_t(L"__PATH"), 0, &varInstaceName, NULL, NULL);
		if (FAILED(hres))
		{
			VariantClear(&varInstaceName);
			printf("\n__PATH Error: %d", GetLastError());
			return FALSE;
		}
	
		pObj->Release();
		pObj = NULL;
	
		IWbemClassObject* pClass = NULL;
		hres = m_pSvc->GetObject(_bstr_t(L"AcpiTest_MULong"),/* ClassName */
			0,
			NULL,
			&pClass,
			NULL);
		if (FAILED(hres))
		{
			VariantClear(&varInstaceName);
			printf("\nAcpiTest_MULong Error: %d", GetLastError());
			return FALSE;
		}
	
		IWbemClassObject* pInParamsDefinition = NULL;
		hres = pClass->GetMethod(_bstr_t(L"GetSetULong"), /* MethodName */
			0,
			&pInParamsDefinition,
			NULL);
		if (FAILED(hres))
		{
			VariantClear(&varInstaceName);
			printf("\nGetSetULong Error: %d", GetLastError());
			return FALSE;
		}
	
		pClass->Release();
		pClass = NULL;
	
		IWbemClassObject* pClassInstance = NULL;
		hres = pInParamsDefinition->SpawnInstance(0, &pClassInstance);
		pInParamsDefinition->Release();
		pInParamsDefinition = NULL;
	
		// Create the values for the in parameters
		TCHAR szCmd[20] = { NULL };
		wsprintf(szCmd, L"%I64u", ullAddr);
		//printf("\nAddr: %0IX", ullAddr);
	
		VARIANT varCommand;
		VariantInit(&varCommand);
		varCommand.vt = VT_BSTR;
		varCommand.bstrVal = _bstr_t(szCmd);
		hres = pClassInstance->Put(L"Data", 0, &varCommand, CIM_UINT64);
		if (FAILED(hres))
		{
			VariantClear(&varCommand);
			VariantClear(&varInstaceName);
			printf("\nData Error: %d", GetLastError());
			return FALSE;
		}
	
		// Execute Method
		IWbemClassObject* pOutParams = NULL;
		hres = m_pSvc->ExecMethod(varInstaceName.bstrVal,
			_bstr_t(L"GetSetULong"), /* MethodName*/
			0,
			NULL,
			pClassInstance,
			&pOutParams,
			NULL);
		if (FAILED(hres))
		{
			VariantClear(&varCommand);
			VariantClear(&varInstaceName);
			pClassInstance->Release();
			printf("\nGetSetULong1 Error: %d", GetLastError());
			return FALSE;
		}
	
		// use the following code.  The return value will be in &varReturn
		VARIANT varReturnValue;
		VariantInit(&varReturnValue);
		hres = pOutParams->Get(_bstr_t(L"Return"), 0, &varReturnValue, NULL, 0);
		if (FAILED(hres))
		{
			VariantClear(&varReturnValue);
			VariantClear(&varInstaceName);
			pClassInstance->Release();
			return FALSE;
		}
	
		*lpVal = varReturnValue.bVal;
		//printf("--> %#02x", *lpVal);
	
		// Clean up
		VariantClear(&varCommand);
		VariantClear(&varInstaceName);
		VariantClear(&varReturnValue);
		pClassInstance->Release();
		pOutParams->Release();
	
		return TRUE;
	}
	
	bool WmiWriteEcRam(ULONG Addr, ULONG Value)
	{
		Value <<= 16;
		Addr = Value + Addr;
	
		// Step 6: --------------------------------------------------
		// Use the IWbemServices pointer to make requests of WMI ----
		IEnumWbemClassObject *pEnum = NULL;
		LRESULT hres = m_pSvc->ExecQuery(_bstr_t(L"WQL"), //Query Language
			_bstr_t(L"Select * from AcpiTest_MULong"),    //Query to Execute
			WBEM_FLAG_RETURN_IMMEDIATELY,                 //Make a semi-synchronous call
			NULL,                                         //Context
			&pEnum                                        //Enumeration Interface
		);
	
		ULONG ulReturned;
		IWbemClassObject *pObj = NULL;
	
		//Get the Next Object from the collection 
		hres = pEnum->Next(WBEM_INFINITE, //Timeout
			1,                            //No of objects requested
			&pObj,                        //Returned Object 
			&ulReturned                   //No of object returned
		);
	
		if (FAILED(hres))
		{
			printf("\nWBEM_INFINITE Error: %d", GetLastError());
			return FALSE;
		}
	
		// Get Instance name path
		VARIANT varInstaceName;
		VariantInit(&varInstaceName);
		hres = pObj->Get(_bstr_t(L"__PATH"), 0, &varInstaceName, NULL, NULL);
		if (FAILED(hres))
		{
			VariantClear(&varInstaceName);
			printf("\n__PATH Error: %d", GetLastError());
			return FALSE;
		}
	
		pObj->Release();
		pObj = NULL;
	
		IWbemClassObject* pClass = NULL;
		hres = m_pSvc->GetObject(_bstr_t(L"AcpiTest_MULong"),/* ClassName */
			0,
			NULL,
			&pClass,
			NULL);
		if (FAILED(hres))
		{
			VariantClear(&varInstaceName);
			printf("\nAcpiTest_MULong Error: %d", GetLastError());
			return FALSE;
		}
	
		IWbemClassObject* pInParamsDefinition = NULL;
		hres = pClass->GetMethod(_bstr_t(L"GetSetULong"), /* MethodName */
			0,
			&pInParamsDefinition,
			NULL);
		if (FAILED(hres))
		{
			VariantClear(&varInstaceName);
			printf("\nGetSetULong Error: %d", GetLastError());
			return FALSE;
		}
	
		pClass->Release();
		pClass = NULL;
	
		IWbemClassObject* pClassInstance = NULL;
		hres = pInParamsDefinition->SpawnInstance(0, &pClassInstance);
		pInParamsDefinition->Release();
		pInParamsDefinition = NULL;
	
		// Create the values for the in parameters
		TCHAR szCmd[20] = { NULL };
		wsprintf(szCmd, L"%I64u", Addr);
		//printf("\nAddr: %0X", Addr);
	
		VARIANT varCommand;
		VariantInit(&varCommand);
		varCommand.vt = VT_BSTR;
		varCommand.bstrVal = _bstr_t(szCmd);
		hres = pClassInstance->Put(L"Data", 0, &varCommand, CIM_UINT64);
		if (FAILED(hres))
		{
			VariantClear(&varCommand);
			VariantClear(&varInstaceName);
			printf("\nData Error: %d", GetLastError());
			return FALSE;
		}
	
		// Execute Method
		IWbemClassObject* pOutParams = NULL;
		hres = m_pSvc->ExecMethod(varInstaceName.bstrVal,
			_bstr_t(L"GetSetULong"), /* MethodName*/
			0,
			NULL,
			pClassInstance,
			&pOutParams,
			NULL);
		if (FAILED(hres))
		{
			VariantClear(&varCommand);
			VariantClear(&varInstaceName);
			pClassInstance->Release();
			printf("\nGetSetULong1 Error: %d", GetLastError());
			return FALSE;
		}
	
		// use the following code.  The return value will be in &varReturn
		VARIANT varReturnValue;
		VariantInit(&varReturnValue);
		hres = pOutParams->Get(_bstr_t(L"Return"), 0, &varReturnValue, NULL, 0);
		if (FAILED(hres))
		{
			VariantClear(&varReturnValue);
			VariantClear(&varInstaceName);
			pClassInstance->Release();
			return FALSE;
		}
	
		BYTE lpVal = varReturnValue.bVal;
		//printf("--> %#02x", lpVal);
	
		// Clean up
		VariantClear(&varCommand);
		VariantClear(&varInstaceName);
		VariantClear(&varReturnValue);
		pClassInstance->Release();
		pOutParams->Release();
		//printf("\nExecMethod done.");
		return TRUE;
	}
#ifdef __cplusplus
}
#endif